using System;
using System.Runtime.CompilerServices;

namespace Framework
{
    public struct AsyncFTaskMethodBuilder
    {
        private FTaskCompletionSource m_TCS;
        private Action m_MoveNext;

        // 1. Static Create method.
        public static AsyncFTaskMethodBuilder Create()
        {
            AsyncFTaskMethodBuilder builder = new AsyncFTaskMethodBuilder();
            return builder;
        }

        // 2. TaskLike Task property.
        public FTask Task
        {
            get
            {
                if (this.m_TCS != null)
                {
                    return this.m_TCS.Task;
                }
                if (this.m_MoveNext == null)
                {
                    return FTask.CompletedTask;
                }
                this.m_TCS = new FTaskCompletionSource();
                return this.m_TCS.Task;
            }
        }

        // 3. SetException
        public void SetException(Exception exception)
        {
            if (this.m_TCS == null)
            {
                this.m_TCS = new FTaskCompletionSource();
            }
            if (exception is OperationCanceledException ex)
            {
                this.m_TCS.TrySetCanceled(ex);
            }
            else
            {
                this.m_TCS.TrySetException(exception);
            }
        }

        // 4. SetResult
        public void SetResult()
        {
            if (this.m_MoveNext == null)
            {
            }
            else
            {
                if (this.m_TCS == null)
                {
                    this.m_TCS = new FTaskCompletionSource();
                }
                this.m_TCS.TrySetResult();
            }
        }

        // 5. AwaitOnCompleted
        public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
            where TAwaiter : INotifyCompletion
            where TStateMachine : IAsyncStateMachine
        {
            if (this.m_MoveNext == null)
            {
                if (this.m_TCS == null)
                {
                    this.m_TCS = new FTaskCompletionSource();
                }

                // TODO: why need create MoveNextRunner
                var runner = new MoveNextRunner<TStateMachine>();
                this.m_MoveNext = runner.Run;
                runner.StateMachine = stateMachine; // set after create delegate.
            }

            awaiter.OnCompleted(m_MoveNext);
        }

        // 6. AwaitUnsafeOnCompleted
        public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
            where TAwaiter : ICriticalNotifyCompletion
            where TStateMachine : IAsyncStateMachine
        {
            if (this.m_MoveNext == null)
            {   
                if (this.m_TCS == null)
                {
                    this.m_TCS = new FTaskCompletionSource();
                }

                // TODO: why need create MoveNextRunner
                var runner = new MoveNextRunner<TStateMachine>();
                this.m_MoveNext = runner.Run;
                runner.StateMachine = stateMachine; // set after create delegate.
            }

            awaiter.UnsafeOnCompleted(m_MoveNext);
        }

        // 7. Start
        public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
        {
            stateMachine.MoveNext();
        }

        // 8. SetStateMachine
        public void SetStateMachine(IAsyncStateMachine stateMachine)
        {
        }
    }

    public struct AsyncFTaskMethodBuilder<T>
    {
        private T m_Result;
        private FTaskCompletionSource<T> m_TCS;
        private Action m_MoveNext;

        // 1. Static Create method.
        public static AsyncFTaskMethodBuilder<T> Create()
        {
            AsyncFTaskMethodBuilder<T> builder = new AsyncFTaskMethodBuilder<T>();
            return builder;
        }

        // 2. TaskLike Task property.
        public FTask<T> Task
        {
            get
            {
                if (this.m_TCS != null)
                {
                    return this.m_TCS.Task;
                }
                if (this.m_MoveNext == null)
                {
                    return FTask.FromResult(m_Result);
                }
                this.m_TCS = new FTaskCompletionSource<T>();
                return m_TCS.Task;
            }
        }

        // 3. SetException
        public void SetException(Exception exception)
        {
            if (this.m_TCS == null)
            {
                this.m_TCS = new FTaskCompletionSource<T>();
            }
            if (exception is OperationCanceledException ex)
            {
                this.m_TCS.TrySetCanceled(ex);
            }
            else
            {
                this.m_TCS.TrySetException(exception);
            }
        }

        // 4. SetResult
        public void SetResult(T result)
        {
            if (this.m_MoveNext == null)
            {
                this.m_Result = result;
            }
            else
            {
                if (this.m_TCS == null)
                {
                    this.m_TCS = new FTaskCompletionSource<T>();
                }
                this.m_TCS.TrySetResult(result);
            }
        }

        // 5. AwaitOnCompleted
        public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
            where TAwaiter : INotifyCompletion
            where TStateMachine : IAsyncStateMachine
        {
            if (this.m_MoveNext == null)
            {
                if (this.m_TCS == null)
                {
                    this.m_TCS = new FTaskCompletionSource<T>();
                }

                // TODO: why need create MoveNextRunner
                var runner = new MoveNextRunner<TStateMachine>();
                this.m_MoveNext = runner.Run;
                runner.StateMachine = stateMachine; // set after create delegate.
            }

            awaiter.OnCompleted(this.m_MoveNext);
        }

        // 6. AwaitUnsafeOnCompleted
        public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
            where TAwaiter : ICriticalNotifyCompletion
            where TStateMachine : IAsyncStateMachine
        {
            if (this.m_MoveNext == null)
            {   
                if (this.m_TCS == null)
                {
                    this.m_TCS = new FTaskCompletionSource<T>();
                }

                // TODO: why need create MoveNextRunner
                var runner = new MoveNextRunner<TStateMachine>();
                this.m_MoveNext = runner.Run;
                runner.StateMachine = stateMachine; // set after create delegate.
            }

            awaiter.UnsafeOnCompleted(this.m_MoveNext);
        }

        // 7. Start
        public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
        {
            stateMachine.MoveNext();
        }

        // 8. SetStateMachine
        public void SetStateMachine(IAsyncStateMachine stateMachine)
        {
        }
    }
}